#pragma once
#include <string>
#include <ctime>
#include <fstream>
#include <cstring>
#include <sys/types.h>
#include <unistd.h>
#include <stdarg.h>
#include <pthread.h>

#define SCREAM 1
#define FILE 2
enum 
{
    DEBUG = 1,
    INFO,
    WARNING,
    ERROR,
    FATAL
};
//日志消息 [日志等级][pid][filename][filenumber][time] 日志内容
class LMessage
{
public:
    std::string _level;//信息等级
    pid_t _pid;//进程id
    std::string _filename;//所在文件
    int _filenumber;//所在文件行号
    std::string _cur_time; //打印时间
    std::string _message_info;//日志内容
};
//获取当前时间
std::string GetCurTime()
{
    //从过去的那一个时刻到现在累计的秒数
    time_t now = time(nullptr);
    //将时间戳转换成年月日时分秒的字符串
    struct tm* cur_time = localtime(&now);
    char buffer[100];
    snprintf(buffer, sizeof(buffer), "%d-%02d-%02d %02d:%02d:%02d", 
            cur_time->tm_year+1900,
            cur_time->tm_mon+1,
            cur_time->tm_mday,
            cur_time->tm_hour,
            cur_time->tm_min,
            cur_time->tm_sec);
    return buffer;
}

pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;
const std::string g_file = "./log.txt";
//日志
class Log
{
private:
    void FlushToScream(LMessage& message)
    {
        //对指定的锁进行加锁
        pthread_mutex_lock(&mutex1); 
        //[日志等级][pid][filename][filenumber][time] 日志内容
        printf("[%s][%d][%s][%d][%s]%s",
            message._level.c_str(), 
            message._pid,
            message._filename.c_str(),
            message._filenumber,
            message._cur_time.c_str(),
            message._message_info.c_str());
        //对指定的锁进行解锁
        pthread_mutex_unlock(&mutex1);
    }
    void FlushToFile(LMessage& message)
    {
        //对指定的锁进行加锁
        pthread_mutex_lock(&mutex1); 
        //std::ofstream out(_file)//每次写入前先前会先清空文件
        std::ofstream out(_file, std::ios::app);//向文件追加写入
        if(!out.is_open())
            return ;
        char buffer[1024];
        snprintf(buffer, sizeof(buffer), "[%s][%d][%s][%d][%s]%s",
            message._level.c_str(), 
            message._pid,
            message._filename.c_str(),
            message._filenumber,
            message._cur_time.c_str(),
            message._message_info.c_str());
        out.write(buffer, strlen(buffer));
        out.close();
        //对指定的锁进行解锁
        pthread_mutex_unlock(&mutex1);
    }
    void Fussh(LMessage& message)
    {
        if(_type == SCREAM)
        {
            //向显示器中打印
            FlushToScream(message);
        }
        else if(_type == FILE)
        {
            //向文件中打印
            FlushToFile(message);
        }
    }
public:
    Log()//默认是向显示器中打印
    :_type(SCREAM)
    ,_file(g_file)
    {}
    //为了方便将等级名称输出到文件/显示屏中，将数字转换成字符串
    const std::string LevelToString(int level)
    {
        switch(level)
        {
            case DEBUG:
                return "DEBUG";
            case INFO:
                return "INFO";
            case ERROR:
                return "ERROR";
            case FATAL:
                return "FATAL";
            default:
                return "UNKNOW";//表示未知的
        }
    }
    //通过log.LogMessage("xx",12,INFO,"ctear %d thread success",pid)这种形式将消息写进日志中
    //通过外部传一个消息，由于外部有时会需要传一个可变参数，message_info在传的时候需要设计成可变参数
    void LogMessage(const std::string filename, int filenumber, int level, const char* format, ...)
    {
        LMessage message;
        message._level = LevelToString(level);
        message._pid = getpid();
        message._filename = filename;
        message._filenumber = filenumber;
        message._cur_time = GetCurTime();

        //定义一个ap
        va_list ap;
        //初始化ap，让ap指向可变参数
        va_start(ap,format);
        char info[1024];
        //将格式化形式传进来，可变参数传进来，自动将转换成字符串放到字符数组中
        vsnprintf(info, sizeof(info), format, ap);
        //销毁ap
        va_end(ap);
        message._message_info = info;

        //将消息写入到文件或显示器;
        Fussh(message);
    }    
    ~Log()
    {
        pthread_mutex_destroy(&mutex1);
        pthread_mutex_destroy(&mutex2);
    }
    void FlusshScream()
    {
        _type = SCREAM;
    }
    void FlusshFile()
    {
       _type = FILE;
    }
private:
    int _type;
    const std::string _file;
};

Log lg;
#define LOG(level, format, ...) do{ lg.LogMessage(__FILE__, __LINE__, level, format, ##__VA_ARGS__); }while(0)
#define Scream() do{ lg.FlusshScream(); }while(0)
#define File() do{ lg.FlusshFile(); }while(0)